import { AstNode } from "./Ast";
import { NodeCompiler } from "./NodeCompiler";
import { sprintf } from 'sprintf-js'

export class AstCompiler {

  static readonly compileIndent = 2

  static indent(level: number): string {
    return ''.padStart(level * AstCompiler.compileIndent, ' ')
  }

  /**
   * compileAst 编译Ast树。目前此函数的成分不纯，包含了一些Process
   * 节点的子节点排布逻辑， 未来考虑进行重构
   * 
   * 调用树中各个节点的编译指令，并且排列他们的输出。形成代码。
   * @param ast 编译的目标语法树
   * @param level 代码层级 - 层级越高，缩进越多
   * @returns 
   */
  static compile(ast: AstNode, level = 0): string {
    // console.log(`编译了 Ast - ${ast.node.prototype.name}`);

    // 当前节点的编译结果
    const selfResult = `${this.indent(level)}${NodeCompiler.compile(ast.node, ast.graph)}\n`

    /**
     * 接下来进行子节点的递归编译
     */

    /** 没有子节点了，到了程序的末尾 */
    if (ast.children.length === 0) {
      // console.log(`${ast.node.prototype.name}返回了(1) - ${selfResult}`);
      return selfResult
    }

    /** 主分支 */
    const sameLevel = ast.children.find(x => x.depth === ast.depth)
    const sameLevelResult = sameLevel ? AstCompiler.compile(sameLevel, level) : ''

    /** 深度分支 */
    const deeperLevels = ast.children.filter(x => x.depth > ast.depth)

    /** 没有深度分支就直接编译返回 */
    if (deeperLevels.length === 0) {
      // console.log(`${ast.node.prototype.name}返回了(2) - ${selfResult}${sameLevelResult}`);
      return `${selfResult}${sameLevelResult}`
    }

    /** 深度分支：根据GraphNode槽的顺序进行重排，用undefined作为占位 */
    const passCode = `${this.indent(level + 1)}pass\n`
    const deeperLevelResultArr = ast.node.getExtraOutProcessSlots()
      .map(slot => deeperLevels.find(_a => _a.node.getProcessIn()?.peer === slot))
      .map(x => x ? AstCompiler.compile(x, level + 1) : passCode)

    const temp = ast.node.prototype.branchTemplate!
    const deeperLevelResult = sprintf(temp, {
      process: deeperLevelResultArr,
      indent: this.indent(level)
    })

    // console.log(`${ast.node.prototype.name}返回了(3) - ${selfResult}${deeperLevelResult}${sameLevelResult}`);
    // console.log('selfResult', selfResult);
    // console.log('deeperLevelResult', deeperLevelResult);
    // console.log('sameLevelResult', sameLevelResult);
    
    return `${selfResult}${deeperLevelResult}${sameLevelResult}`
  }
}

